#ifndef THREADING_H_
#define THREADING_H_
#include "modules.h"



class ARunnable {
protected:
	pthread_t t;
public:
	virtual ~ARunnable() {}
	
	virtual void start()=0;
	void wait() {
		pthread_join(t,NULL);
	}
	virtual void run()=0;
	static void* tfunc(void* args) {
		ARunnable *t = (ARunnable*)args;
	        	t->run();
	        	return 0;
	        }
};


class Runnable :public ARunnable {

public:
	virtual ~Runnable() {}
	
	void start() {

		int rc = pthread_create(&t, NULL, ARunnable::tfunc, (void *)this); 
		if (rc)
		{
			printf("ERROR; return code from pthread_create() is %d\n", rc);
			exit(-1);
		}
	}

};

const std::string MODULE_TIME_TIMER_NAME = "MODULE_TIME_TIMER_NAME";

class TimerConfig : public ModuleConfig {
public:
	
	Prop<unsigned long> *lastTime;
	Prop<int> *precisionMillis;
	Prop<long> *time;
	TimerConfig(DataPool *_pool,std::string _name,int _precisionMillis):ModuleConfig(_pool,_name,MODULE_TIME_TIMER_NAME) {
		lastTime = new Prop<unsigned long>(LONG,"lastTime",this);
		precisionMillis = new Prop<int>(INT,"precisionMillis",this);
		time = new Prop<long>(LONG,"time",this);
		precisionMillis->set(_precisionMillis);
		lastTime->set(0);
		time->set(0);
	}
};

class Timer : public Runnable, public Module {

public:
	
	Timer(TimerConfig *_conf):Module(_conf){}
	Timer():Module(){}
	
	inline TimerConfig* conf() {
		return (TimerConfig*)config;
	}
	void run() {
		
		while (conf()->time->get()<50) {
			usleep(10);
			unsigned long current = TimeHelper::currentTimeMillis();
			unsigned long last=conf()->lastTime->get();
			int precisionMillis = conf()->precisionMillis->get();
			if (last==0) {
				conf()->lastTime->set(current);
			}
			else if (current >= (last+precisionMillis)) {
				conf()->time->set(conf()->time->get()+1);
				conf()->lastTime->set(last+precisionMillis);
				std::cout<<"time:"<<current<<" "<< TimeHelper::currentTimeMicros()<<" "<<pthread_self() <<"\n";
			}
			
			
		}
	}
};


//////////////////////////////////////////////SelectorListener::

const std::string MODULE_TIME_SELECTOR = "MODULE_TIME_SELECTOR";

class TimeSelector : public Module{
protected:
	Listener *lis;
public:
	TimeSelector():Module(){}
	TimeSelector(ModuleConfig *_conf):Module(_conf){
		_conf->moduleName->set(MODULE_TIME_SELECTOR);
	}
	virtual ~TimeSelector(){}
	void setListener(Listener *_lis) {
		lis=_lis;
	}
	virtual void test(long _id, unsigned int _index,Value<long> *c){
		select(_id,_index,c);
	}
	virtual void select(long _id,unsigned int _index,Value<long> *c) {
		lis->longChanged(_id,_index,c);
	}
};

const std::string MODULE_TIME_THREADED_SELECTOR = "MODULE_TIME_THREADED_SELECTOR";

class ThreadedTimeSelector : public TimeSelector , public Runnable {
protected:
	long id;
	unsigned int index;
	Value<long> *c;
public:
	ThreadedTimeSelector():TimeSelector(){}
	ThreadedTimeSelector(ModuleConfig *_conf):TimeSelector(_conf){
		_conf->moduleName->set(MODULE_TIME_THREADED_SELECTOR);
	}
	virtual ~ThreadedTimeSelector(){}

	virtual void test(long _id, unsigned int _index,Value<long> *c){
		select(_id,_index,c);
	}
	void select(long _id,unsigned int _index,Value<long> *_c) {
		id = _id;
		index = _index;
		c=_c;
		start();
	}
	void run() {
		lis->longChanged(id,index,c);
	}
};



const std::string MODULE_TIME_SELECTOR_LISTENER = "MODULE_TIME_SELECTOR_LISTENER";

class TimeSelectorListener : public Runnable, public Module, public Listener {
private: 
	std::list<TimeSelector*> list;
	long id;
	unsigned int index;
	Value<long> *c;
public:
	TimeSelectorListener():Module() {
		acceptedValues = LONG;
	}
	TimeSelectorListener(ModuleConfig *_conf):Module(_conf){
		_conf->moduleName->set(MODULE_TIME_SELECTOR_LISTENER);
		acceptedValues = LONG;
	}
	short longChanged(long _id,unsigned int _index,Value<long> *_c) {
		id = _id;
		index = _index;
		c=_c;
		start();
		return true;
	}
	void run() {
		std::list<TimeSelector*>::const_iterator it;
		for (it = list.begin(); it!=list.end();++it) {
			(*it)->test(id,index,c);
		}
	}
	void addTimeSelector(TimeSelector *t) {
		list.push_back(t);
	}
};


const std::string MODULE_TIME_DEBUG_LONG_LISTENER = "MODULE_TIME_DEBUG_LONG_LISTENER";


class TestLongListener : public Listener, public Module {
public:
	~TestLongListener() {}
	TestLongListener() {
		acceptedValues = LONG;
	}
	TestLongListener(ModuleConfig *_conf):Module(_conf){
			_conf->moduleName->set(MODULE_TIME_DEBUG_LONG_LISTENER);
			acceptedValues = LONG;
	}
	short longChanged(long _id,unsigned int _index,Value<long> *c) {
		printf("longChanged: %d\n",(int)c->getValue());
		std::cout<<"threadedTime: "<<TimeHelper::currentTimeMicros()<<" "<<pthread_self()<<"\n";
		return true;
	}
	
};

class TimeImport {
public:
	static void importModule(DataPool *_pool) {
		_pool->registerCreator(MODULE_TIME_TIMER_NAME,&(create<Timer>));
		_pool->registerCreator(MODULE_TIME_SELECTOR,&(create<TimeSelector>));
		_pool->registerCreator(MODULE_TIME_THREADED_SELECTOR,&(create<ThreadedTimeSelector>));
		_pool->registerCreator(MODULE_TIME_SELECTOR_LISTENER,&(create<TimeSelectorListener>));
		_pool->registerCreator(MODULE_TIME_DEBUG_LONG_LISTENER,&(create<TestLongListener>));
		_pool->registerCreator("module",&(create<Module>));
	}
};

#endif /*THREADING_H_*/
